This R Markdown documents the data cleaning analysis carried out on the dataset.
Data Cleaning
In carrying out data cleaning, a few key variables come to mind:
- Uniqueness of Players
- Age
- Consistency in nationality
Uniqueness of Players
# the number of rows of the data for league is consistent
map_dbl(list(league_defense, league_shooting, league_passing,
league_goalkeeping), nrow)
[1] 5554 5554 5554 413
# However, the number of players in tournaments exhibit inconsistencies
map_dbl(list(tournament_defense, tournament_shooting, tournament_passing,
tournament_goalkeeping), nrow)%>%
set_names(c("tournament_defense","tournament_shooting",
"tournament_passing","tournament_goalkeeping"))
tournament_defense tournament_shooting tournament_passing
488 2015 488
tournament_goalkeeping
129
The next step is to ensure that the distinct players are the same. We first try the league players. We can confirm that the list of shooting, passing and defense players are indeed correct.
check_list_equal<- function(out, input){
if(!all(out==input)){
return(done(FALSE))
}
input
}
# If output is not consistent
list(2,2,3,4)%>%
reduce(check_list_equal)
[1] FALSE
# There are 3429 league players that are consistent.
league_players<-list(league_defense, league_shooting, league_passing)%>%
map(function(db) db%>%
distinct(Player)%>%
arrange(Player))%>%
reduce(check_list_equal)
league_players
Tournament player data cleaning is slightly more complicated, mainly as shooting and goalkeeping data is available for 2020 while the other data sets only have 2021 data.
map(list(tournament_defense, tournament_shooting, tournament_passing,
tournament_goalkeeping),
~.[["Year"]]%>%
unique())%>%
set_names(c("tournament_defense","tournament_shooting",
"tournament_passing","tournament_goalkeeping"))
$tournament_defense
[1] 2021
$tournament_shooting
[1] 2020 2021
$tournament_passing
[1] 2021
$tournament_goalkeeping
[1] 2020 2021
First check that the defense and passing have the same players. followed by checking if shooting is indeed a subset of the larger player pool.
# For tournament
tournament_players<-list(tournament_defense, tournament_passing)%>%
map(function(db) db%>%
distinct(Player, Year, Nation)%>%
arrange(Player,Year,Nation))%>%
reduce(check_list_equal)
check_player_pool<-function(big_pool, comparator,
pool_col="Player", comparator_col="Player"){
big_pool<-distinct(big_pool, across(matches(pool_col)))
comparator<-distinct(comparator, across(matches(comparator_col)))
if(all(comparator[[comparator_col]]%in% big_pool[[pool_col]])){
message("All tournament shooting players are in the player pool.")
}
else{
warning("Not all tournament shooting players are in the player pool.")
print(
paste("Of the", nrow(comparator), "Players, only",
sum(comparator[[comparator_col]]%in% big_pool[[pool_col]]),
"are in the bigger pool."))
comparator[!comparator[[comparator_col]]%in% big_pool[[pool_col]],]
}
}
check_player_pool(tournament_players,
tournament_shooting%>%
filter(Year==2021))
All tournament shooting players are in the player pool.
Age Issue
A separate issue observed is that ages in the tournaments are not consistent:
# Does age increase over time?
# Confusingly, it decreases over time.
tournament_shooting%>%
group_by(Player)%>%
mutate(occurance = n())%>%
filter(occurance>1)%>%
arrange(Player,Year, Age)%>%
select(Player,Year, Age)
tournament_shooting%>%
group_by(Player)%>%
mutate(occurance = n())%>%
filter(occurance>1)%>%
arrange(Player,Year, Age)%>%
pivot_wider(id_cols = Player,names_from = Year, values_from = Age,
names_prefix = "year_")%>%
mutate(diff = year_2021 - year_2020)%>%
group_by(diff)%>%
summarise(count = n())
tournament_goalkeeping%>%
group_by(Player)%>%
mutate(occurance = n())%>%
filter(occurance>1)%>%
arrange(Player,Year, Age)%>%
select(Player,Year, Age)
This is not the case for league data, however.
map(list(league_defense, league_shooting, league_passing,
league_goalkeeping),
function(db) {
db%>%
group_by(Player)%>%
mutate(occurance = max(Year)-min(Year))%>%
filter(occurance>=1)%>%
select(Player,Year, Age)%>%
ungroup()%>%
arrange(Player,Year, desc(Age))%>%
distinct()%>%
pivot_wider(names_from = Year, values_from = Age,
names_prefix = "year_")%>%
mutate(diff = year_2021 - year_2020)%>%
group_by(diff)%>%
summarise(count = n())
})
[[1]]
[[2]]
[[3]]
[[4]]
NA
Compare the leagued data with the tournament data:
map(list(tournament_shooting, tournament_goalkeeping),
function(db) {
db%>%
group_by(Player)%>%
mutate(occurance = max(Year)-min(Year))%>%
filter(occurance>=1)%>%
select(Player,Year, Age)%>%
ungroup()%>%
arrange(Player,Year, desc(Age))%>%
distinct()%>%
pivot_wider(names_from = Year, values_from = Age,
names_prefix = "year_")%>%
mutate(diff = year_2021 - year_2020)%>%
group_by(diff)%>%
summarise(count = n())
})%>%
set_names(c("tournament_shooting","tournament_goalkeeping"))
$tournament_shooting
$tournament_goalkeeping
NA
Find the average age difference between calculated and the actual age.
tournament_shooting%>%
mutate(calc_ages=Year - Born)%>%
mutate(diff = calc_ages - Age)%>%
group_by(diff)%>%
summarise(count =n())
league_shooting%>%
mutate(calc_ages=Year - Born)%>%
mutate(diff = calc_ages - Age)%>%
group_by(diff)%>%
summarise(count =n())
The assumption used here is that the age should be standardized to year less birth year. This ensures consistencies between the data set. League ages tend to underestimate the true age, while tournament ages tend to overestimate the ages.
tournament_shooting%>%
select(Player, Year, Age)%>%
rename(age_tourny=Age)%>%
filter(Player %in% league_players$Player)%>%
inner_join(league_defense%>%
arrange(Player, Year, Age)%>%
distinct(Player, Year, Age),
by = c("Player", "Year"))%>%
mutate(diff = age_tourny - Age)%>%
group_by(diff)%>%
summarise(count = n())
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFDQopDQpzb3VyY2UoIjAwIHNldCB1cC5SIikNCmBgYA0KDQpUaGlzIFIgTWFya2Rvd24gZG9jdW1lbnRzIHRoZSBkYXRhIGNsZWFuaW5nIGFuYWx5c2lzIGNhcnJpZWQgb3V0IG9uIHRoZSBkYXRhc2V0Lg0KDQpgYGANCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4NCmBgYA0KDQojIyBEYXRhIENsZWFuaW5nDQpJbiBjYXJyeWluZyBvdXQgZGF0YSBjbGVhbmluZywgYSBmZXcga2V5IHZhcmlhYmxlcyBjb21lIHRvIG1pbmQ6DQoNCiogVW5pcXVlbmVzcyBvZiBQbGF5ZXJzDQoqIEFnZQ0KKiBDb25zaXN0ZW5jeSBpbiBuYXRpb25hbGl0eQ0KDQojIyMgVW5pcXVlbmVzcyBvZiBQbGF5ZXJzDQpgYGB7ciwgZWNobyA9IFR9DQojIHRoZSBudW1iZXIgb2Ygcm93cyBvZiB0aGUgZGF0YSBmb3IgbGVhZ3VlIGlzIGNvbnNpc3RlbnQNCm1hcF9kYmwobGlzdChsZWFndWVfZGVmZW5zZSwgbGVhZ3VlX3Nob290aW5nLCBsZWFndWVfcGFzc2luZywgDQogICAgICAgICAgICAgbGVhZ3VlX2dvYWxrZWVwaW5nKSwgbnJvdykNCg0KIyBIb3dldmVyLCB0aGUgbnVtYmVyIG9mIHBsYXllcnMgaW4gdG91cm5hbWVudHMgZXhoaWJpdCBpbmNvbnNpc3RlbmNpZXMNCm1hcF9kYmwobGlzdCh0b3VybmFtZW50X2RlZmVuc2UsIHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfcGFzc2luZywgDQogICAgICAgICAgICAgdG91cm5hbWVudF9nb2Fsa2VlcGluZyksIG5yb3cpJT4lDQogIHNldF9uYW1lcyhjKCJ0b3VybmFtZW50X2RlZmVuc2UiLCJ0b3VybmFtZW50X3Nob290aW5nIiwNCiAgICAgICAgICAgICAidG91cm5hbWVudF9wYXNzaW5nIiwidG91cm5hbWVudF9nb2Fsa2VlcGluZyIpKQ0KYGBgDQoNClRoZSBuZXh0IHN0ZXAgaXMgdG8gZW5zdXJlIHRoYXQgdGhlIGRpc3RpbmN0IHBsYXllcnMgYXJlIHRoZSBzYW1lLiBXZSBmaXJzdCB0cnkgdGhlIGxlYWd1ZSBwbGF5ZXJzLiBXZSBjYW4gY29uZmlybSB0aGF0IHRoZSBsaXN0IG9mIHNob290aW5nLCBwYXNzaW5nIGFuZCBkZWZlbnNlIHBsYXllcnMgYXJlIGluZGVlZCBjb3JyZWN0Lg0KDQpgYGB7ciwgZWNobyA9IFR9DQpjaGVja19saXN0X2VxdWFsPC0gZnVuY3Rpb24ob3V0LCBpbnB1dCl7DQogIGlmKCFhbGwob3V0PT1pbnB1dCkpew0KICAgIHJldHVybihkb25lKEZBTFNFKSkNCiAgfQ0KICBpbnB1dA0KfQ0KDQojIElmIG91dHB1dCBpcyBub3QgY29uc2lzdGVudA0KbGlzdCgyLDIsMyw0KSU+JQ0KICByZWR1Y2UoY2hlY2tfbGlzdF9lcXVhbCkNCg0KIyBUaGVyZSBhcmUgMzQyOSBsZWFndWUgcGxheWVycyB0aGF0IGFyZSBjb25zaXN0ZW50LiANCmxlYWd1ZV9wbGF5ZXJzPC1saXN0KGxlYWd1ZV9kZWZlbnNlLCBsZWFndWVfc2hvb3RpbmcsIGxlYWd1ZV9wYXNzaW5nKSU+JQ0KICBtYXAoZnVuY3Rpb24oZGIpIGRiJT4lIA0KICAgICAgZGlzdGluY3QoUGxheWVyKSU+JQ0KICAgICAgYXJyYW5nZShQbGF5ZXIpKSU+JQ0KICByZWR1Y2UoY2hlY2tfbGlzdF9lcXVhbCkNCmxlYWd1ZV9wbGF5ZXJzDQpgYGANCg0KVG91cm5hbWVudCBwbGF5ZXIgZGF0YSBjbGVhbmluZyBpcyBzbGlnaHRseSBtb3JlIGNvbXBsaWNhdGVkLCBtYWlubHkgYXMgc2hvb3RpbmcgYW5kIGdvYWxrZWVwaW5nIGRhdGEgaXMgYXZhaWxhYmxlIGZvciAyMDIwIHdoaWxlIHRoZSBvdGhlciBkYXRhIHNldHMgb25seSBoYXZlIDIwMjEgZGF0YS4NCg0KYGBge3J9DQptYXAobGlzdCh0b3VybmFtZW50X2RlZmVuc2UsIHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfcGFzc2luZywgDQogICAgICAgICAgICAgdG91cm5hbWVudF9nb2Fsa2VlcGluZyksIA0KICAgICAgICB+LltbIlllYXIiXV0lPiUNCiAgICAgICAgICB1bmlxdWUoKSklPiUNCiAgc2V0X25hbWVzKGMoInRvdXJuYW1lbnRfZGVmZW5zZSIsInRvdXJuYW1lbnRfc2hvb3RpbmciLA0KICAgICAgICAgICAgICJ0b3VybmFtZW50X3Bhc3NpbmciLCJ0b3VybmFtZW50X2dvYWxrZWVwaW5nIikpDQpgYGANCg0KRmlyc3QgY2hlY2sgdGhhdCB0aGUgZGVmZW5zZSBhbmQgcGFzc2luZyBoYXZlIHRoZSBzYW1lIHBsYXllcnMuICBmb2xsb3dlZCBieSBjaGVja2luZyBpZiBzaG9vdGluZyBpcyBpbmRlZWQgYSBzdWJzZXQgb2YgdGhlIGxhcmdlciBwbGF5ZXIgcG9vbC4NCg0KYGBge3J9DQojIEZvciB0b3VybmFtZW50DQp0b3VybmFtZW50X3BsYXllcnM8LWxpc3QodG91cm5hbWVudF9kZWZlbnNlLCB0b3VybmFtZW50X3Bhc3NpbmcpJT4lDQogIG1hcChmdW5jdGlvbihkYikgZGIlPiUgDQogICAgICBkaXN0aW5jdChQbGF5ZXIsIFllYXIsIE5hdGlvbiklPiUNCiAgICAgIGFycmFuZ2UoUGxheWVyLFllYXIsTmF0aW9uKSklPiUNCiAgcmVkdWNlKGNoZWNrX2xpc3RfZXF1YWwpDQoNCmNoZWNrX3BsYXllcl9wb29sPC1mdW5jdGlvbihiaWdfcG9vbCwgY29tcGFyYXRvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb29sX2NvbD0iUGxheWVyIiwgY29tcGFyYXRvcl9jb2w9IlBsYXllciIpew0KICBiaWdfcG9vbDwtZGlzdGluY3QoYmlnX3Bvb2wsIGFjcm9zcyhtYXRjaGVzKHBvb2xfY29sKSkpDQogIGNvbXBhcmF0b3I8LWRpc3RpbmN0KGNvbXBhcmF0b3IsIGFjcm9zcyhtYXRjaGVzKGNvbXBhcmF0b3JfY29sKSkpDQogIA0KICBpZihhbGwoY29tcGFyYXRvcltbY29tcGFyYXRvcl9jb2xdXSVpbiUgYmlnX3Bvb2xbW3Bvb2xfY29sXV0pKXsNCiAgICBtZXNzYWdlKCJBbGwgdG91cm5hbWVudCBzaG9vdGluZyBwbGF5ZXJzIGFyZSBpbiB0aGUgcGxheWVyIHBvb2wuIikNCiAgfQ0KICBlbHNlew0KICAgIHdhcm5pbmcoIk5vdCBhbGwgdG91cm5hbWVudCBzaG9vdGluZyBwbGF5ZXJzIGFyZSBpbiB0aGUgcGxheWVyIHBvb2wuIikNCiAgICBwcmludCgNCiAgICAgIHBhc3RlKCJPZiB0aGUiLCBucm93KGNvbXBhcmF0b3IpLCAiUGxheWVycywgb25seSIsDQogICAgICAgICAgc3VtKGNvbXBhcmF0b3JbW2NvbXBhcmF0b3JfY29sXV0laW4lIGJpZ19wb29sW1twb29sX2NvbF1dKSwNCiAgICAgICAgICAiYXJlIGluIHRoZSBiaWdnZXIgcG9vbC4iKSkNCiAgICBjb21wYXJhdG9yWyFjb21wYXJhdG9yW1tjb21wYXJhdG9yX2NvbF1dJWluJSBiaWdfcG9vbFtbcG9vbF9jb2xdXSxdDQogIH0NCn0NCg0KY2hlY2tfcGxheWVyX3Bvb2wodG91cm5hbWVudF9wbGF5ZXJzLCANCiAgICAgICAgICAgICAgICAgIHRvdXJuYW1lbnRfc2hvb3RpbmclPiUNCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKFllYXI9PTIwMjEpKQ0KDQpgYGANCg0KIyMjIEFnZSBJc3N1ZQ0KQSBzZXBhcmF0ZSBpc3N1ZSBvYnNlcnZlZCBpcyB0aGF0IGFnZXMgaW4gdGhlIHRvdXJuYW1lbnRzIGFyZSBub3QgY29uc2lzdGVudDoNCg0KYGBge3IgdG91cm5hbWVudF9hZ2VfaXNzdWVzfQ0KIyBEb2VzIGFnZSBpbmNyZWFzZSBvdmVyIHRpbWU/DQojIENvbmZ1c2luZ2x5LCBpdCBkZWNyZWFzZXMgb3ZlciB0aW1lLg0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBzZWxlY3QoUGxheWVyLFllYXIsIEFnZSkNCg0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBwaXZvdF93aWRlcihpZF9jb2xzID0gUGxheWVyLG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IEFnZSwNCiAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gInllYXJfIiklPiUNCiAgbXV0YXRlKGRpZmYgPSB5ZWFyXzIwMjEgLSB5ZWFyXzIwMjApJT4lDQogIGdyb3VwX2J5KGRpZmYpJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KdG91cm5hbWVudF9nb2Fsa2VlcGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBzZWxlY3QoUGxheWVyLFllYXIsIEFnZSkNCmBgYA0KDQpUaGlzIGlzIG5vdCB0aGUgY2FzZSBmb3IgbGVhZ3VlIGRhdGEsIGhvd2V2ZXIuDQoNCmBgYHtyIGxlYWd1ZV9hZ2Vfc3VtbWFyeX0NCm1hcChsaXN0KGxlYWd1ZV9kZWZlbnNlLCBsZWFndWVfc2hvb3RpbmcsIGxlYWd1ZV9wYXNzaW5nLCANCiAgICAgICAgICAgICBsZWFndWVfZ29hbGtlZXBpbmcpLCANCiAgICBmdW5jdGlvbihkYikgew0KICAgICAgZGIlPiUNCiAgICAgICAgZ3JvdXBfYnkoUGxheWVyKSU+JQ0KICAgICAgICBtdXRhdGUob2NjdXJhbmNlID0gbWF4KFllYXIpLW1pbihZZWFyKSklPiUNCiAgICAgICAgZmlsdGVyKG9jY3VyYW5jZT49MSklPiUNCiAgICAgICAgc2VsZWN0KFBsYXllcixZZWFyLCBBZ2UpJT4lDQogICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICBhcnJhbmdlKFBsYXllcixZZWFyLCBkZXNjKEFnZSkpJT4lDQogICAgICAgIGRpc3RpbmN0KCklPiUNCiAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gQWdlLA0KICAgICAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAieWVhcl8iKSU+JQ0KICAgICAgICBtdXRhdGUoZGlmZiA9IHllYXJfMjAyMSAtIHllYXJfMjAyMCklPiUNCiAgICAgICAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KICAgIH0pDQpgYGANCg0KQ29tcGFyZSB0aGUgbGVhZ3VlZCBkYXRhIHdpdGggdGhlIHRvdXJuYW1lbnQgZGF0YToNCg0KYGBge3IgdG91cm5hbWVudF9hZ2Vfc3VtbWFyeX0NCm1hcChsaXN0KHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfZ29hbGtlZXBpbmcpLCANCiAgICBmdW5jdGlvbihkYikgew0KICAgICAgZGIlPiUNCiAgICAgICAgZ3JvdXBfYnkoUGxheWVyKSU+JQ0KICAgICAgICBtdXRhdGUob2NjdXJhbmNlID0gbWF4KFllYXIpLW1pbihZZWFyKSklPiUNCiAgICAgICAgZmlsdGVyKG9jY3VyYW5jZT49MSklPiUNCiAgICAgICAgc2VsZWN0KFBsYXllcixZZWFyLCBBZ2UpJT4lDQogICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICBhcnJhbmdlKFBsYXllcixZZWFyLCBkZXNjKEFnZSkpJT4lDQogICAgICAgIGRpc3RpbmN0KCklPiUNCiAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gQWdlLA0KICAgICAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAieWVhcl8iKSU+JQ0KICAgICAgICBtdXRhdGUoZGlmZiA9IHllYXJfMjAyMSAtIHllYXJfMjAyMCklPiUNCiAgICAgICAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KICAgIH0pJT4lDQogIHNldF9uYW1lcyhjKCJ0b3VybmFtZW50X3Nob290aW5nIiwidG91cm5hbWVudF9nb2Fsa2VlcGluZyIpKQ0KYGBgDQoNCkZpbmQgdGhlIGF2ZXJhZ2UgYWdlIGRpZmZlcmVuY2UgYmV0d2VlbiBjYWxjdWxhdGVkIGFuZCB0aGUgYWN0dWFsIGFnZS4NCg0KYGBge3J9DQp0b3VybmFtZW50X3Nob290aW5nJT4lDQogIG11dGF0ZShjYWxjX2FnZXM9WWVhciAtIEJvcm4pJT4lDQogIG11dGF0ZShkaWZmID0gY2FsY19hZ2VzIC0gQWdlKSU+JQ0KICBncm91cF9ieShkaWZmKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPW4oKSkNCg0KbGVhZ3VlX3Nob290aW5nJT4lDQogIG11dGF0ZShjYWxjX2FnZXM9WWVhciAtIEJvcm4pJT4lDQogIG11dGF0ZShkaWZmID0gY2FsY19hZ2VzIC0gQWdlKSU+JQ0KICBncm91cF9ieShkaWZmKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPW4oKSkNCmBgYA0KDQpUaGUgYXNzdW1wdGlvbiB1c2VkIGhlcmUgaXMgdGhhdCB0aGUgYWdlIHNob3VsZCBiZSBzdGFuZGFyZGl6ZWQgdG8geWVhciBsZXNzIGJpcnRoIHllYXIuIFRoaXMgZW5zdXJlcyBjb25zaXN0ZW5jaWVzIGJldHdlZW4gdGhlIGRhdGEgc2V0LiBMZWFndWUgYWdlcyB0ZW5kIHRvIHVuZGVyZXN0aW1hdGUgdGhlIHRydWUgYWdlLCB3aGlsZSB0b3VybmFtZW50IGFnZXMgdGVuZCB0byBvdmVyZXN0aW1hdGUgdGhlIGFnZXMuDQoNCmBgYHtyfQ0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBzZWxlY3QoUGxheWVyLCBZZWFyLCBBZ2UpJT4lDQogIHJlbmFtZShhZ2VfdG91cm55PUFnZSklPiUNCiAgZmlsdGVyKFBsYXllciAlaW4lIGxlYWd1ZV9wbGF5ZXJzJFBsYXllciklPiUNCiAgaW5uZXJfam9pbihsZWFndWVfZGVmZW5zZSU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKFBsYXllciwgWWVhciwgQWdlKSU+JQ0KICAgICAgICAgICAgICBkaXN0aW5jdChQbGF5ZXIsIFllYXIsIEFnZSksDQogICAgICAgICAgICBieSA9IGMoIlBsYXllciIsICJZZWFyIikpJT4lDQogIG11dGF0ZShkaWZmID0gYWdlX3RvdXJueSAtIEFnZSklPiUNCiAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KYGBgDQoNCg==